from pygame.constants import K_a , K_d , K_w , K_s
from pygame.surface import Surface
from pygame import key as pk

from config import win_size , player_speed
from libs.ZDisplay import ZDisplay
from libs.ZJson import ZJson
from libs.ZNode import ZNode
from libs.components.ZPlayer import ZPlayer
from libs.fun import cut_image , create_image_alpha , play_music , create_image


class ZTiled ( ZNode ) :
    def __init__ ( self , father , filename ) :
        super ( ZTiled , self ).__init__ ( father , father.rect )
        self.father = father
        self.filename = filename
        self.data = ZJson ( filename ).get ( )
        self.layers = [ ]
        self.texttures = self.data [ 'tilesets' ]
        self.running = True
        self.respose = True
        self.texture = self.cuttextures ( )
        self.walls = [ ]
        for name , data in self.data.items ( ) :
            if name == 'layers' :
                for x in data :
                    if not x [ 'name' ] == "walls" :
                        self.layers.append ( TiledLayer ( self , x ) )
                    else :
                        self.walls.append ( TiledWall ( self , x ) )
        self.objs = [ ]
        self.player = ZPlayer ( self )
        self.add ( self.player )
        if self.data [ 'properties' ] [ 'music' ] :
            url = self.data [ 'properties' ] [ 'music' ] [ 6 : ]
            play_music ( url )
        self.control = Controller ( self.player , self.walls , self.layers )

    def cuttextures ( self ) :
        arrs = [ ]
        res = [ ]
        for x in self.texttures :
            url = x [ 'image' ] [ 6 : ]
            end = url.split ( "." ) [ 1 ]
            if end == "png" :
                img = create_image_alpha ( url )
            else :
                img = create_image ( url )
                img.set_alpha ( 120 )
            arrs.append ( cut_image ( img , x [ 'tilewidth' ] , x [ 'tileheight' ] ) )
        for x in arrs :
            res = res + x
        return res

    def draw ( self ) :
        if self.running and self.layers :
            for x in self.layers :
                x.draw ( )
        if self.running and self.walls :
            for x in self.walls :
                x.draw ( )
        # self.control.draw ( )
        super ( ZTiled , self ).draw ( )

    def move_left ( self ) :
        self.control.left ( )

    def move_right ( self ) :
        self.control.right ( )

    def move_up ( self ) :
        self.control.up ( )

    def move_down ( self ) :
        self.control.down ( )

    def move ( self ) :
        keys = pk.get_pressed ( )
        if keys [ K_a ] :
            self.move_left ( )
        else :
            self.control.reset ( )
        if keys [ K_d ] :
            self.move_right ( )
        else :
            self.control.reset ( )
        if keys [ K_w ] :
            self.move_up ( )
        else :
            self.control.reset ( )
        if keys [ K_s ] :
            self.move_down ( )
        else :
            self.control.reset ( )


class TiledLayer ( ZDisplay ) :
    def __init__ ( self , father , data ) :
        super ( TiledLayer , self ).__init__ ( father , [ 32 * data [ 'width' ] , 32 * data [ 'height' ] ] )
        self.name = data [ 'name' ]
        self.opacity = data [ 'opacity' ]
        self.width = data [ 'width' ]
        self.height = data [ 'height' ]
        self.type = data [ "type" ]
        self.visible = data [ 'visible' ]
        self.x = data [ 'x' ]
        self.y = data [ 'y' ]
        self.can_move = True
        self._pos = self.x , self.y
        self.texture = self.father.texture
        self.alpha = Surface ( [ 32 , 32 ] ).convert ( )
        self.alpha.set_alpha ( 0 )
        self.data = [ ]
        data = data [ 'data' ]
        arr = [ ]
        for id , x in enumerate ( data ) :
            if (id + 1) % (self.width) == 0 :
                arr.append ( x )
                self.data.append ( arr )
                arr = [ ]
            else :
                arr.append ( x )
        for x in range ( len ( self.data ) ) :
            for y in range ( len ( self.data [ 0 ] ) ) :
                pos = 32 * y , 32 * x
                id = self.data [ x ] [ y ]
                if not id == 0 :
                    self.image.blit ( self.texture [ self.data [ x ] [ y ] - 1 ] , pos )

    def move_left ( self ) :
        self.rect.move_ip ( player_speed , 0 )

    def move_right ( self ) :
        self.rect.move_ip ( -player_speed , 0 )

    def move_up ( self ) :
        self.rect.move_ip ( 0 , player_speed )

    def move_down ( self ) :
        self.rect.move_ip ( 0 , -player_speed )


class TiledWall ( object ) :
    def __init__ ( self , father , data ) :
        super ( TiledWall , self ).__init__ ( )
        self.father = father
        self.width = int ( data [ 'width' ] )
        self.height = int ( data [ 'height' ] )
        self.data = data [ 'data' ]
        self.name = data [ 'name' ]
        self.data = [ ]
        self.walls = [ ]
        self.cache = [ ]
        self.display = data [ 'visible' ]
        self.color = 255 , 0 , 0
        self.image = self.father.texture [ 24 ]
        from pygame.rect import Rect
        data = data [ 'data' ]
        arr = [ ]
        for id , x in enumerate ( data ) :
            if (id + 1) % (self.width) == 0 :
                arr.append ( x )
                self.data.append ( arr )
                arr = [ ]
            else :
                arr.append ( x )
        for x in range ( len ( self.data ) ) :
            for y in range ( len ( self.data [ 0 ] ) ) :
                pos = 32 * y , 32 * x
                id = self.data [ x ] [ y ]
                if id :
                    self.walls.append ( Rect ( pos , [ 32 , 32 ] ) )

    def draw ( self ) :
        if self.display :
            for x in self.walls :
                self.father.surface.blit ( self.image , x.topleft )

    def update ( self ) :
        pass

    def move_up ( self ) :
        for x in self.walls :
            x.move_ip ( 0 , player_speed )

    def move_down ( self ) :
        for x in self.walls :
            x.move_ip ( 0 , -player_speed )

    def move_left ( self ) :
        for x in self.walls :
            x.move_ip ( player_speed , 0 )

    def move_right ( self ) :
        for x in self.walls :
            x.move_ip ( -player_speed , 0 )


class Controller ( object ) :
    def __init__ ( self , player , walls , layers , events = None ) :
        super ( Controller , self ).__init__ ( )
        self.player = player
        self.walls = walls
        self.layers = layers
        self.events = events
        self.width = self.layers [ 0 ].width
        self.height = self.layers [ 0 ].height
        self.buffer = self.player.area
        self.walls_list = [ ]
        for x in self.walls :
            for y in x.walls :
                self.walls_list.append ( y )

    def up ( self ) :
        mapY = self.layers [ 0 ]._pos [ 1 ]
        playerY = self.player.area.top
        if self.can_up() :
            if mapY < 0 and playerY < int ( win_size [ 1 ] * .4 ) :
                for x in self.layers :
                    x.move_up ( )
                for x in self.walls :
                    x.move_up ( )
            else :
                self.player.move_up ( )

    def down ( self ) :
        mapY = self.layers [ 0 ]._pos [ 1 ]
        playerY = self.player.area.top
        s = -self.height * 32 + int ( win_size [ 1 ] )
        if self.can_down ( ) :
            if mapY > s and playerY > int ( win_size [ 1 ] * .6 ) :
                for x in self.layers :
                    x.move_down ( )
                for x in self.walls :
                    x.move_down ( )
            else :
                self.player.move_down ( )

    def left ( self ) :
        mapX = self.layers [ 0 ]._pos [ 0 ]
        playerX = self.player.area.left
        if self.can_left ( ) :
            if mapX < 0 and playerX < int ( win_size [ 0 ] * .4 ) :
                for x in self.layers :
                    x.move_left ( )
                for x in self.walls :
                    x.move_left ( )
            else :
                self.player.move_left ( )

    def right ( self ) :
        mapX = self.layers [ 0 ]._pos [ 0 ]
        playerX = self.player.area.left
        s = -self.width * 32 + int ( win_size [ 0 ] )
        if self.can_right ( ) :
            if mapX > s and playerX > int ( win_size [ 0 ] * .6 ) :
                for x in self.layers :
                    x.move_right ( )
                for x in self.walls :
                    x.move_right ( )
            else :
                self.player.move_right ( )

    def can_up ( self ) :
        self.buffer = self.player.area.copy ( )
        self.buffer.move_ip ( 0 , -player_speed )
        collide = self.buffer.collidelist ( self.walls_list )
        if collide == -1 or not collide :
            return True
        else :
            return False

    def can_down ( self ) :
        self.buffer = self.player.area.copy ( )
        self.buffer.move_ip ( 0 , player_speed )
        collide = self.buffer.collidelist ( self.walls_list )
        if collide == -1 or not collide :
            return True
        else :
            return False

    def can_left ( self ) :
        self.buffer = self.player.area.copy ( )
        self.buffer.move_ip ( -player_speed , 0 )
        collide = self.buffer.collidelist ( self.walls_list )
        if collide == -1 or not collide :
            return True
        else :
            return False

    def can_right ( self ) :
        self.buffer = self.player.area.copy ( )
        self.buffer.move_ip ( player_speed , 0 )
        collide = self.buffer.collidelist ( self.walls_list )
        if collide == -1 or not collide :
            return True
        else :
            return False

    def reset ( self ) :
        self.buffer = self.player.area

    def draw ( self ) :
        from pygame import draw
        draw.rect ( self.walls.image , [ 255 , 111 , 111 ] , self.buffer , 0 )
